'use strict'

const http = require('http'),
    fs = require('fs');
const fsp = fs.promises


/*--------------------------------解析body数据---------------------------------*/

/*
*Content-Disposition:form-data;name="file";filename="xxx"
*Content-Type:text/plain
*
*@param {string} headerData
*
*/






function parseBodyHeaders(headerData) {
    let hlist = headerData.split('\r\n').filter(p => p.length > 0)

    //解析content-disposotion=>hlist[0]
    //用;分隔，从第二个元素开始的数组
    let csplit = hlist[0].split('; ').slice(1)

    let filename
    let name
    for (let a of csplit) {
        if (a.indexOf('filename') === 0) {
            filename = a.substring(a.indexOf('=') + 2, a.length - 1)
        } else {
            name = a.substring(a.indexOf('=') + 2, a.length - 1)
        }
    }
    let headers = {}
    let ind = 0
    let k
    for (let h of hlist) {
        ind = h.indexOf(':')
        k = h.substring(0, ind).toLowerCase()
        headers[k] = h.substring(ind + 1).trim()
    }

    return {
        filename,
        name,
        headers,
        contentType: headers['content-type'] || ''
    }
    //let contentType = 

}

function parseBody(bodyData, headers) {
    let ctype = headers['content-type']

    let bdy = ctype.substring(ctype.indexOf('=') + 1)

    let crlf_bdy = `\r\n--${bdy}`
    //连续\r\n\r\n的位置，一定是headerData结束位置
    let header_end_index = bodyData.indexOf('\r\n\r\n',
        crlf_bdy.length);

    let headerData = bodyData.toString('utf8',
        crlf_bdy.length,
        header_end_index);

    //解析文件头信息
    let hd = parseBodyHeaders(headerData)

    let file = {
    
        start: header_end_index + 4,
        end: bodyData.indexOf(crlf_bdy, header_end_index),
        ...hd

    }

    file.length = file.end - file.start

    return file


}

//解析文件扩展名
function parseExtName(filename) {
    // console.log(filename)
    if (filename.length < 2) {
        return ''
    }
    let nsplit = filename.split('.').filter(p => p.length > 0)

    if (nsplit.length < 2) return '';
    return `.${nsplit[nsplit.length - 1]}`

}
/*--------------------------------解析body数据  END--------------------------*/






/*
.jpg .jpeg -> image/ipeg
.png -> image/png
.gif -> image/gif
.ico -> image/x-icon
.txt ->text/plain
.json -> text/json or application/json
.xml -> text/xml or application/xml
*/
let html = `
<!DOCTYPE html>
<html>
    <head>
        <title>FIRST HTTP</title>
    </head>
    <body>
        HELLO
    </body>
</html>
`

let routerTable = {

    GET: {
        '/': async (ctx) => {
            ctx.response.setHeader('content-type',
                'text/html;charset=utf-8')
            ctx.end(html)

        },
        '/test': async (ctx) => {
            let status = parseInt(Math.random() * 200) + 199
            ctx.response.statusCode = status
            ctx.response.end('test end')
        },
        '/upload': async (ctx) => {
            let stm = fs.createReadStream('./upload.html')
            stm.pipe(ctx.response, { end: false })
            stm.on('end', () => {
                ctx.end()
            })
        }

    },

    POST: {

        '/data': async (ctx) => {

            let file = parseBody(ctx.rawBody, ctx.headers);
            console.log(file)

            if (!ctx.rawBody) {
                ctx.end();
                return;
            }

            let fname = `${Date.now()}-${Math.random()}`
                + `${parseExtName(file.filename)}`;
            let fh = await fsp.open(fname, 'w+')
            let fret = await fh.write(ctx.rawBody, file.start, file.length, 0)
            fh.close();
            ctx.end(`${fret.bytesWritten}`)
            // ctx.end(ctx.rawBody)


            // let bufferList = [1]
            // let bufferLength = 0
            // request.on('data', chunk => {
            //     bufferLength += chunk.length
            //     bufferList.push(chunk)
            // })
            // let bodyData = null;
            // request.on('end', () => {
            //     if (bufferList.length > 0) {
            //         bodyData = Buffer.concat(bufferList, bufferLength)
            //         bufferList = []
            //     }

            //     let file = parseBody(bodyData,request.headers)

            //     console.log(file);

            //     if (bodyData) {
            //         response.end(bodyData)
            //     } else {
            //         response.end()
            //     }
            // })

        }
    }
}



http.createServer(async (request, response) => {



    if (!routerTable[request.url]) {

        let rm = routerTable[request.method]
        let usplit = request.url.split('?');

        let pathname = usplit[0];
        let querystring = usplit[1] || ''

        if (!rm || !rm[pathname]) {
            response.statusCode = 404
            response.end('page not found')
            return
        }
        //ctx是context的缩写，意为请求上下文，就是一个
        //打包了各种数据和方法的对象
        let ctx = {
            method: request.method,
            path: pathname,
            search: querystring,
            headers: request.headers,
            request: request,
            response: response,
            end: (data, encoding = 'utf8') => {
                response.end(data, encoding)
            },
            write: (data, encoding = 'utf8') => {
                response.write(data, encoding)
            }, totalLength: 0,
            rawBody: null,
            bufferList: []

        }
        // console.log(ctx);
        //GET HEAD TRACE OPTIONS 不发送数据（专门计算不正确请求），解析层已经过滤
        //POST PUT PATCH（更新） 可以发送数据（看首字母是P还是D）

        //实现使用async才能用await,先new Promise后await
        //本质协成，但是可以断开做别的回来继续
        await new Promise((rv, rj) => {//promise在微队列
            if ('PD'.indexOf(request.method[0]) >= 0) {
                request.on('data', chunk => {
                    ctx.bufferList.push(chunk)
                    ctx.totalLength += chunk.length
                })
            } else {
                request.on('data', data => {
                    response.statusCode = 400
                    response.end('bad request', () => {
                        request.destroy()//直接销毁，客户端收到的直接关闭
                    })
                })
            }
            //文件描述符 ==> 一切皆文件
            let resolved = false;
            request.on('end', () => {
                resolved = true;
                rv();
            })
            request.on('close', () => {
                (!resolved) && rv();
            })
        })
        //前后不同的出发顺序
        if (!response.writable || response.writableEnded) {
            return //能写
        }
        if (ctx.bufferList.length > 0) {
            ctx.rawBody = Buffer.concat(ctx.bufferList, ctx.totalLength)
            ctx.bufferList = []
        }
        console.log(ctx.rawBody)
        rm[pathname](ctx)
        //   rm[request.url](request,response)
    }
    //routerTable[ request.url ](request,response)
}).listen(3456)